﻿/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1
 *
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 *
 * The Original Code is tyzoh.jp code.
 *
 * The Initial Developer of the Original Code is Kazutaka Kurihara.
 * Portions created by the Initial Developer are Copyright (C) 2007
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * ***** END LICENSE BLOCK ***** */
using System;
using System.Collections;
using System.Timers;
using Microsoft.Win32;
using System.IO;
using System.Diagnostics;
using System.Text;
using System.Runtime.InteropServices;
//using System.Reflection;

//using NUnit.Framework;

/* [Available class]
 * Util  .... about  Path & String, DynamicProperty, SingleInstance, LaunchCommand
 * TimerExeQueueSystem
 * Bisection
 * Vector2
 * Matrix22
 * QArrayList
 * AssocArray
*/

namespace Qurihara.QBasic
{
	public class Util
    {
        #region LaunchCommandLineTool

        public static void EasyLaunch(string exeName, string argLine)
        {
            System.Diagnostics.Process process = new System.Diagnostics.Process();
            process.StartInfo.FileName = exeName;
            process.StartInfo.Arguments = argLine;
            process.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal;
            process.Start();
        }

        // by http://support.microsoft.com/default.aspx?scid=kb;ja;305994
		public static string LaunchCommand(string exeName, string argsLine, int timeoutSeconds)
		{
			StreamReader outputStream = StreamReader.Null;
			string output = "";
			bool success = false;
			try
			{
				Process newProcess = new Process();
				newProcess.StartInfo.FileName = exeName;
				newProcess.StartInfo.Arguments = argsLine;
				newProcess.StartInfo.UseShellExecute = false;
				newProcess.StartInfo.CreateNoWindow = true;
				newProcess.StartInfo.RedirectStandardOutput = true;
				newProcess.Start();
				if (0 == timeoutSeconds)
				{
					outputStream = newProcess.StandardOutput;
					output = outputStream.ReadToEnd();
					newProcess.WaitForExit();
				}
				else
				{
					success = newProcess.WaitForExit(timeoutSeconds * 1000);
	
					if (success)
					{
						outputStream = newProcess.StandardOutput;
						output = outputStream.ReadToEnd();
					}
					else
					{
						output = exeName + " の終了待ちで " + timeoutSeconds + " 秒でタイム アウトしました。";
					}
				}
			}
			catch(Exception e)
			{
				throw (new Exception(exeName + " の実行中にエラーが発生しました。",e));
			}
			finally
			{
				outputStream.Close();
			}
			return output;
		}
		#endregion

		#region Dynamic Property Handling
		public static bool IsPropertyTrue(string keyname)
		{
			System.Collections.Specialized.NameValueCollection settings
				= System.Configuration.ConfigurationSettings.AppSettings;
			if(settings[keyname] == null) return false;
			if (settings[keyname].Equals("true")) return true;
			return false;
		}
		public static bool GetStringProperty(string keyname, ref string valuename)
		{
			System.Collections.Specialized.NameValueCollection settings
				= System.Configuration.ConfigurationSettings.AppSettings;
			if (settings[keyname] == null) return false;
			valuename = settings[keyname];
			return true;
		}

		public static bool GetInt32Property(string keyname, ref int valuename)
		{
			System.Collections.Specialized.NameValueCollection settings
				= System.Configuration.ConfigurationSettings.AppSettings;
			if (settings[keyname] == null) return false;
			try
			{
				valuename = Convert.ToInt32(settings[keyname]);
			}
			catch(Exception e)
			{
				return false;
			}
			return true;
		}

		public static bool GetFloatProperty(string keyname, ref float valuename)
		{
			System.Collections.Specialized.NameValueCollection settings
				= System.Configuration.ConfigurationSettings.AppSettings;
			if (settings[keyname] == null) return false;
			try
			{
				valuename = Convert.ToSingle(settings[keyname]);
			}
			catch(Exception e)
			{
				return false;
			}
			return true;
		}

		public static bool GetDoubleProperty(string keyname, ref double valuename)
		{
			System.Collections.Specialized.NameValueCollection settings
				= System.Configuration.ConfigurationSettings.AppSettings;
			if (settings[keyname] == null) return false;
			try
			{
				valuename = Convert.ToDouble(settings[keyname]);
			}			
			catch(Exception e)
			{
				return false;
			}
			return true;
		}
		#endregion

		public static string getTimeNowString() 
		{//現在日時を表す文字列を得る
			string a = DateTime.Now.ToString();
			a = a.Replace("/",".");
			a = a.Replace(" ",".");
			a =a.Replace(":",".");
			return a;
		}
		public static string getFileNameWithoutPath(string filename)
		{//ファイルのフルパスからファイル名だけを抽出
			return System.IO.Path.GetFileName(filename);
		}
		public static string getPathWithoutFileName(string filename)
		{//ファイルのフルパスからファイル名を除いた部分だけを抽出
			return System.IO.Path.GetDirectoryName(filename);
		}
		public static bool IsFileNameWithoutPath(string filename)
		{//ファイル名が単独ファイル名か、何らかのパスを含むか調べる
			return !System.IO.Path.IsPathRooted(filename);
		}

		public static string [] SplitCharsLimitedStringArray(string s,int rowLimit)
		{// stringを、一行の文字数を制限して配列に分ける。
			ArrayList outArray = new ArrayList();
			//string tmp;
			while(s != null)
			{
				if (s.Length <= rowLimit)
				{
					outArray.Add(s);
					s = null;
				}
				else
				{
					//tmp = s.Substring(0,rowLimit);
					outArray.Add(s.Substring(0,rowLimit));
					s = s.Substring(rowLimit);
				}
			}
			return (string [])outArray.ToArray(typeof(string));
		}

		public static void TrimFirst(ref string s, char param)
		{
			while(s.StartsWith(param.ToString()))
			{
				if (s.Length == 1) 
				{
					s = null;
					break;
				}
				s = s.Substring(1);
			}

		}
        //protected static Char [] littleLetterList = {
        //                                 'ぁ',
        //                                 'ぃ',
        //                                 'ぅ',
        //                                 'ぇ',
        //                                 'ぉ',
        //                                 'っ',
        //                                 'ゃ',
        //                                 'ゅ',
        //                                 'ょ',
        //                                 'ゎ',
        //                                                'ァ',
        //                                                'ィ',
        //                                                'ゥ',
        //                                                'ェ',
        //                                                'ォ',
        //                                                'ッ',
        //                                                'ャ',
        //                                                'ュ',
        //                                                'ョ',
        //                                                'ヮ',
        //};
        //public static bool IsLittleLetter(Char c)
        //{
        //    for(int i=0;i<Util.littleLetterList.Length;i++)
        //    {
        //        if (c.Equals(Util.littleLetterList[i]))
        //        {
        //            return true;
        //        }
        //    }
        //    return false;
        //}

		[DllImport("shlwapi.dll", CharSet = CharSet.Auto)]    
		private static extern bool PathCompactPathEx(
			StringBuilder pszOut, 
			string pszPath,
			int cchMax, 
			int reserved); 

		public static string GetShortDisplayName(string longName, int maxLen)
		{
			StringBuilder pszOut = new StringBuilder(maxLen + maxLen + 2);  // for safety

			if ( PathCompactPathEx(pszOut, longName, maxLen, 0) )
			{
				return pszOut.ToString();
			}
			else
			{
				return longName;
			}
		}

	}

//	[TestFixture]
//	public class UtilTest
//	{
//		string s,s2;
//		[SetUp]
//		public void Init()
//		{
//			s = @"C:\hoge\foo\bar.xml";
//			s2 = @"bar.xml";
//		}
//		[Test]
//		public void getFileNameWithoutPathTest()
//		{
//			string t = Util.getFileNameWithoutPath(s);
//			Assertion.AssertEquals("bar.xml",t);
//		}
//		[Test]
//		public void getPathNameWithoutFileTest()
//		{
//			string t = Util.getPathWithoutFileName(s);
//			Assertion.AssertEquals("C:\\hoge\\foo",t);
//		}
//		[Test]
//		public void IsFileNameWithoutPathTest()
//		{
//			bool b = Util.IsFileNameWithoutPath(s);
//			Assertion.AssertEquals(false,b);
//			bool b2 = Util.IsFileNameWithoutPath(s2);
//			Assertion.AssertEquals(true,b2);
//		}
//	}

    public class DelayedActivator
    {
        public static int IntervalForCheck = 100; // millisec
        protected DateTime timeToActivate;
        protected int milliSecToActivate;
        public int MilliSecToActivate
        {
            get
            {
                return milliSecToActivate;
            }
            set
            {
                milliSecToActivate = value;
            }
        }
        public void Update()
        {
            Stop();
            timeToActivate = DateTime.Now.Add(new TimeSpan(0, 0, 0, 0, milliSecToActivate));
            timer.Enabled = true;
            timer.Start();
        }

        public event System.EventHandler Activated;
        protected System.Timers.Timer timer;
        public DelayedActivator()
        {
            timer = new Timer();
            timer.Interval = IntervalForCheck;
            timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
            timer.Enabled = false;
        }
        public void Start()
        {
            //timer.Enabled = true;
            Update();
            //timer.Start();
        }
        public void StartAndWaitForUpdate()
        {
            timeToActivate = DateTime.MaxValue;
            timer.Enabled = true;
            timer.Start();
        }
        public void Stop()
        {
            timer.Stop();
            timer.Enabled = false;
        }
        public void Dispose()
        {
            Stop();
            timer = null;
        }

        void timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            if (DateTime.Now > timeToActivate)
            {
                if (Activated != null) Activated(this, new EventArgs());
                timer.Enabled = false;
            }
        }
    }

	public class TimerExeQueue
	{
		protected ArrayList theQ;
		protected long timeToWaitTick;
		protected long timeToExpireTick;
		protected TimerExeQueueElement theElement = null;
		protected System.Timers.Timer timer;
		protected bool enable;
		
		public TimerExeQueue(long timeToWaitMilliSec,long timeToExpireMilliSec,double routineInterval)
		{
			theQ = new ArrayList();
			timeToWaitTick = timeToWaitMilliSec*10000;
			timeToExpireTick = timeToExpireMilliSec*10000;

			timer = new System.Timers.Timer();
			timer.Elapsed+=new ElapsedEventHandler(RoutineCheck);
			// Set the Interval 
			timer.Interval=routineInterval;
			enable = false;
		}

		public void Enable(bool e)
		{
			enable = e;
			timer.Enabled = e;
		}
		public void Clear()
		{
			timer.Enabled = false;
			theQ.Clear();
			// theElement を適切に終了。
			theElement = null;
			if (enable) timer.Enabled = true;
		}

		public bool IsEmpty()
		{
			if(theQ.Count == 0 && theElement == null)
			{
				return true;
			}
			return false;
		}

		public void Update(TimerExeQueueElement element)
		{
			timer.Enabled = false;
			if (element.Equals(theElement) == false)
			{ // if it is not executing now
				element.timeToExe = this.GetExeTime();
				element.timeToExpire = this.GetExpireTime();
				if(theQ.Contains(element))
				{
					int ind = theQ.IndexOf(element);
					TimerExeQueueElement teqe = (TimerExeQueueElement)theQ[ind];
					theQ.RemoveAt(ind);
					theQ.Add(teqe);
				}
				else
				{ // if it is new member
					this.Enqueue(element);
				}
			}
			else
			{// if it is now executing, ignore update
				/*
				if (theElement != null && theElement.state == TimerExeQueueElement.State.WAITING)
				{
					this.Enqueue(element);
					theElement = null;
				}
				*/
			}
			if (enable)	timer.Enabled  = true;
		}
		protected void Enqueue(TimerExeQueueElement element)
		{
			//element.invalidate = false;
			element.enable = true;
			element.state = TimerExeQueueElement.State.WAITING;
			theQ.Add(element);
		}

		public void RoutineCheck(object source, ElapsedEventArgs e)
		{
			long now;
			timer.Enabled = false;
			while(true)
			{
				if(theElement != null)
				{
					now = this.GetTimeNow();
					if (now >= theElement.timeToExpire)
					{   // 実行終了前に時間切れ。
						if (theElement.state == TimerExeQueueElement.State.EXECUTING)
							theElement.Cancel();
						theElement.state = TimerExeQueueElement.State.CANCELED_OR_EXPIRED;
					}

					if (theElement.state != TimerExeQueueElement.State.WAITING)
					{ //WAITING以外
						if(theElement.state == TimerExeQueueElement.State.EXECUTING)
						{   // 実行終了までまつ。
							break;
						}
						else
						{   // case : state = CANCEL_OR_EXPIRED, EXECUTED 
							// この要素は終了。
							theElement.AfterCare();
							theElement = null;
						}
					}
				}

				// ここより下は、state= WAITINGだけが通る

				if(theElement == null)
				{
					if (theQ.Count == 0) break;
					theElement = (TimerExeQueueElement)(theQ[0]);
					theQ.RemoveAt(0);
				}

				if(theElement.enable)
				{
					//if(!theElement.invalidate)
					//{ // 実行可能なとき
						now = this.GetTimeNow();
						if (theElement.timeToExe <= now)
						{ // 実行可能時間になった
							if (now < theElement.timeToExpire)
							{ //  実行開始
								theElement.state = TimerExeQueueElement.State.EXECUTING;
								theElement.Exe();
							}
							else
							{// 時間切れなので、次のループででExpiredになる。
							}
						}
						else
						{ // まだ実行可能時間ではないのでさよなら
							break;
						}
					//}
					//else
					//{ // updateされているので実行を先送り
					//	this.Update(theElement);
					//	theElement = null;
					//}
				}
				else
				{ // 要素がdisableのときは実行せずキャンセル
					theElement.state = TimerExeQueueElement.State.CANCELED_OR_EXPIRED;
					theElement.AfterCare();
					theElement = null;
				}
			}
			if (enable) timer.Enabled = true;
		}

		protected long GetTimeNow()
		{			
			return DateTime.Now.Ticks;
		}
		protected  long GetExeTime()
		{
			return DateTime.Now.Ticks + timeToWaitTick;
		}
		protected  long GetExpireTime()
		{
			return DateTime.Now.Ticks + timeToExpireTick;
		}
	}
	//********************
	public abstract class TimerExeQueueElement
	{
		public long timeToExe;
		public long timeToExpire;
		public bool enable;
		//public bool invalidate;
		public enum State
		{
			WAITING,
			EXECUTING,
			EXECUTED,
			CANCELED_OR_EXPIRED,
		}
		public State state;

		public virtual void Exe()
		{
			// do some work
			state=State.EXECUTED;
		}

		public virtual void Cancel()
		{
			// do some work to cancel
			state=State.CANCELED_OR_EXPIRED;
		}

		public virtual void AfterCare()
		{
			// called before the element is set null
		}

		public void NotifyExecuted()
		{
			state=State.EXECUTED;
		}
	}

	//*******************
	public class TimerExeQueueElementRecyclable : TimerExeQueueElement
	{
		protected Queue queue;

		public void SetQueue(Queue q)
		{
			queue = q;
		}
		public override void AfterCare()
		{
			queue.Enqueue(this);
		}
	}
	//*********************
	public class TimerExeQueueSystem : TimerExeQueue
	{
		protected Queue workerQ;
		protected TimerExeQueueElementRecyclable [] elements;
		public TimerExeQueueSystem(long timeToWaitMilliSec
			,long timeToExpireMilliSec,double routineInterval
			,TimerExeQueueElementRecyclable [] elems) : base(timeToWaitMilliSec,timeToExpireMilliSec,routineInterval)
		{
			workerQ = new Queue();
			elements = elems;
			this.init();
		}

		public TimerExeQueueElementRecyclable GetWorkerElement()
		{
			if (workerQ.Count == 0) return null;
			return (TimerExeQueueElementRecyclable)workerQ.Dequeue();
		}

		public void init()
		{
			workerQ.Clear();
			if(elements != null)
			{
				for(int i=0;i<elements.Length;i++)
				{
					if (elements[i] != null)	
					{
						elements[i].SetQueue(workerQ);
						workerQ.Enqueue(elements[i]);
					}
				}
			}

			this.Clear();
		}
	}
	
	//*****************
	public interface BisectionFunction
	{
		int func(int x);
	}
	//*****************
	public class Bisection
	{
		
		public enum State
		{
			solved,
			error,
		}
		public static State Solve(BisectionFunction bf, ref int l,ref int r)
		{
			int fl,fr;
			fl = bf.func(l);fr= bf.func(r);
			if (fl * fr > 0) return State.error;
			while (Math.Abs(l-r) > 1)
			//while (l != r)
				{
				fr= bf.func(r);
				fl = bf.func((l + r)/2);
				if (fr == 0)
				{
					l = r;
					break;
				}
				if (fl == 0)
				{
					int a = (l + r) / 2;
					l = r = a;
					break;
				}
				if (fl * fr >0)
				{
					if ((l + r) % 2 == 0)
					{
						r = (l + r) / 2;
					}
					else
					{
						r = (l + r) / 2 + 1;
					}
				}
				else
				{
					l = (l + r)/2;
				}
			}
			return State.solved;
		}
	}

	// ************************
	public class Vector2 
	{
		public double [] x;
		protected double norm;
		public Vector2()
		{
			x = new double[2];
			x[0] = 0;
			x[1] = 0;
			norm = 0;
		}
		public Vector2(double x_,double y_) : this()
		{
			x[0] = x_;
			x[1] = y_;	
			norm = 0;   
		}
		
		public void setVec(double x_,double y_)
		{
			x[0] = x_;
			x[1] = y_;
		}
		public void setVec(Vector2 v)
		{
			x[0] = v.x[0];
			x[1] = v.x[1];
		}
		public void mul(double k)
		{
			x[0] *= k;
			x[1] *= k;
		}
		public void mul(Matrix22 m,ref Vector2 output ) // m * this = output  
		{
			output.x[0] = m.Col[0].x[0] * this.x[0] + m.Col[1].x[0] * this.x[1];
			output.x[1] = m.Col[0].x[1] * this.x[0] + m.Col[1].x[1] * this.x[1];
		}
		public void mul(Matrix22 m) // m * this >> this
		{
			Vector2 tmp = new Vector2();
			this.mul(m,ref tmp);
			this.setVec(tmp);
		}

		public double Norm
		{
			get
			{
				return norm;
			}
		}
		public void calcNorm()
		{
			norm = Math.Sqrt(x[0]*x[0]+x[1]*x[1]);
		}
		public void unitize()
		{
			calcNorm();
			x[0] /= norm;
			x[1] /= norm;
			norm = 1.0;
		}
		public double dotProduct(Vector2 kakerareru) // check the order. x.y -> x.dotProduct(y)
		{
			return this.x[0] * kakerareru.x[0]
				+ this.x[1] * kakerareru.x[1];
		}
		public bool IsZero()
		{
			return (this.x[0] == 0 && this.x[1] == 0);
		}

	}
	public class Matrix22
	{
		Vector2 [] col;
		//Vector2 lin1,lin2;

		public Matrix22()
		{
			col = new Vector2[2];
			col[0] = new Vector2();
			col[1] = new Vector2();
		}

		public Matrix22(Vector2 v1_,Vector2 v2_):this()  // note that vectors are seen as collumns.
		{
			col[0].setVec(v1_);
			col[1].setVec(v2_);
		}
		public Matrix22(double m11_, double m12_, double m21_, double m22_) :this()
		{
			col[0].setVec(m11_,m21_);
			col[1].setVec(m12_,m22_);
		}

		public void setMat(double m11,double m12, double m21, double m22)
		{
			col[0].setVec(m11,m21);
			col[1].setVec(m12,m22);

		}
		public Vector2[]  Col
		{
			get
			{
				return col;
			}
		}

		public void trans()
		{
			double swap;
			swap = col[1].x[0];
			col[1].x[0] = col[0].x[1];
			col[0].x[1] = swap;
		}
		public double det()
		{
			return col[0].x[0] * col[1].x[1] - col[1].x[0] * col[0].x[1];
		}
		public void mul(double d)
		{
			col[0].mul(d);
			col[1].mul(d);
		}
		public void mul(Matrix22 kakerareru,ref Matrix22 output)
		{
			kakerareru.col[0].mul(this,ref output.col[0]);
			kakerareru.col[1].mul(this,ref output.col[1]);
		}
		public void inv()
		{
			double d = det();
			double swap;

			col[1].x[0] = -col[1].x[0];
			col[0].x[1] = -col[0].x[1];
			swap = col[0].x[0];
			col[0].x[0] = col[1].x[1];
			col[1].x[1] =swap;
			mul(1.0/d);						
		}
	}

	// *****************
	public class QArrayList : ArrayList
	{
		protected IEnumerator enume;
		public event EventHandler ListChanged;
		public bool EnableListChanged = true;

		public QArrayList() : base()
		{
		}

		#region override
		public override int Add(object value)
		{
			int result = base.Add(value);
            if (ListChanged != null && EnableListChanged) ListChanged(this, new EventArgs());
			return result;
		}
		public override void AddRange(ICollection c)
		{
			base.AddRange (c);
            if (ListChanged != null && EnableListChanged) ListChanged(this, new EventArgs());
		}
		public override void Clear()
		{
			base.Clear ();
            if (ListChanged != null && EnableListChanged) ListChanged(this, new EventArgs());
		}
		public override void Insert(int index, object value)
		{
			base.Insert (index, value);
            if (ListChanged != null && EnableListChanged) ListChanged(this, new EventArgs());
		}
		public override void InsertRange(int index, ICollection c)
		{
			base.InsertRange (index, c);
            if (ListChanged != null && EnableListChanged) ListChanged(this, new EventArgs());
		}
		public override void Remove(object obj)
		{
			base.Remove (obj);
            if (ListChanged != null && EnableListChanged) ListChanged(this, new EventArgs());
		}
		public override void RemoveAt(int index)
		{
			base.RemoveAt (index);
            if (ListChanged != null && EnableListChanged) ListChanged(this, new EventArgs());
		}
		public override void RemoveRange(int index, int count)
		{
			base.RemoveRange (index, count);
            if (ListChanged != null && EnableListChanged) ListChanged(this, new EventArgs());
		}
		public override void Reverse()
		{
			base.Reverse ();
            if (ListChanged != null && EnableListChanged) ListChanged(this, new EventArgs());
		}
		public override void Reverse(int index, int count)
		{
			base.Reverse (index, count);
            if (ListChanged != null && EnableListChanged) ListChanged(this, new EventArgs());
		}
		public override void SetRange(int index, ICollection c)
		{
			base.SetRange (index, c);
            if (ListChanged != null && EnableListChanged) ListChanged(this, new EventArgs());
		}
		public override void Sort()
		{
			base.Sort ();
            if (ListChanged != null && EnableListChanged) ListChanged(this, new EventArgs());
		}
		public override void Sort(int index, int count, IComparer comparer)
		{
			base.Sort (index, count, comparer);
            if (ListChanged != null && EnableListChanged) ListChanged(this, new EventArgs());
		}
		public override void Sort(IComparer comparer)
		{
			base.Sort (comparer);
            if (ListChanged != null && EnableListChanged) ListChanged(this, new EventArgs());
		}
		#endregion

		public void Remove(QArrayList fe)
		{
			enume = fe.GetEnumerator();
			while (enume.MoveNext())
			{
				this.Remove(enume.Current);
			}
			if (ListChanged != null && EnableListChanged) ListChanged(this,new EventArgs());
		}
		public void Set(QArrayList fel)
		{
			this.Clear();
			this.Add(fel);
			if (ListChanged != null && EnableListChanged) ListChanged(this,new EventArgs());
		}
		public void Add(QArrayList fel)
		{
			enume = fel.GetEnumerator();
			while(enume.MoveNext())
			{
				this.Add(enume.Current);
			}
			if (ListChanged != null && EnableListChanged) ListChanged(this,new EventArgs());
		}
		public void AddNewElement(Object o)
		{
			if (this.IndexOf(o) == -1) this.Add(o);
			if (ListChanged != null && EnableListChanged) ListChanged(this,new EventArgs());
		}
		public void AddNewElement(QArrayList fel)
		{
			QArrayList addList = new QArrayList();
			enume = fel.GetEnumerator();
			Object fe;
			while (enume.MoveNext())
			{
				fe = enume.Current;
				if (this.IndexOf(fe) == -1)
				{
					addList.Add(fe);
				}
			}
			this.Add(addList);
			if (ListChanged != null && EnableListChanged) ListChanged(this,new EventArgs());

			/*
			enume = addList.GetEnumerator();
			while(enume.MoveNext())
			{
				fe = (FigureElement)enume.Current;
				this.Add(fe);
			}
			*/
		}
		public bool ContainsAllElementsOf(QArrayList fel)
		{
			enume = fel.GetEnumerator();
			Object fe;
			while (enume.MoveNext())
			{
				fe = enume.Current;
				if (this.IndexOf(fe) == -1)
				{
					return false;
				}
			}
			return true;
		}
		public bool ContainsSameElementsOf(QArrayList fel)
		{
			return (this.ContainsAllElementsOf(fel) 
				&& fel.ContainsAllElementsOf(this));
		}
		public void AddSubListTypeOf(System.Type t,QArrayList originalList)
		{
			enume = originalList.GetEnumerator();
			while(enume.MoveNext())
			{
				if (enume.Current.GetType() == t)
				{
					this.Add(enume.Current);
				}
			}
			if (ListChanged != null && EnableListChanged) ListChanged(this,new EventArgs());

		}
	}

	// **************************
	public class AssocArrayList
	{
		ArrayList keyList;
		ArrayList valueList;
		public AssocArrayList()
		{
			keyList = new ArrayList();
			valueList = new ArrayList();
		}

		public void Clear()
		{
			keyList.Clear();
			valueList.Clear();
		}
		public void Add(string key,string val)
		{
			if (!keyList.Contains(key))
			{// ない場合は付け加え
				keyList.Add(key);
				valueList.Add(val);
			}
			else
			{// ある場合は置き換える。
				int index = keyList.IndexOf(key);
				string  tmp = (string)valueList[index];
				tmp = val;
			}
		}
		public bool Remove(string key)
		{
			int index = keyList.IndexOf(key);
			if (index > -1)
			{ // ある。
				keyList.RemoveAt(index);
				valueList.RemoveAt(index);
				return true;
			}
			else
			{ // ない
				return false;
			}
		}
		public bool Exist(string key)
		{
			return keyList.Contains(key);
		}
		public string Value(string key)
		{
			string outs = null;
			for (int i=0;i<keyList.Count;i++)
			{
				if (((string)keyList[i]).Equals(key))
				{
					outs = (string)valueList[i]; 
					break;
				}
			}
			return outs;
		}
	}

}